package com.github.florent37.shapeofview.shapes;


import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.StackLayout;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;

import com.github.florent37.shapeofview.ShapeOfView;
import com.github.florent37.shapeofview.manager.ClipPathManager;
import com.github.florent37.shapeofview.manager.ResourceTable;

import java.util.Optional;

/**
 * DottedEdgesCutCornerView
 *
 * @since 2021-07-26
 */
public class DottedEdgesCutCornerView extends StackLayout implements Component.EstimateSizeListener, Component.DrawTask {

    private Optional<Display> display = null;
    private Paint paint = new Paint();
    public static final int POSITION_NONE = 0;
    public static final int POSITION_BOTTOM = 1;
    public static final int POSITION_TOP = 2;
    public static final int POSITION_LEFT = 4;
    public static final int POSITION_RIGHT = 8;

    private final Rect rectF = new Rect();

    private float topLeftCutSize = 0f;
    private float topRightCutSize = 0f;
    private float bottomRightCutSize = 0f;
    private float bottomLeftCutSize = 0f;

    private int dotEdgePosition;
    private int shape_position;

    private float dotRadius = 0f;
    private float dotSpacing = 0f;
    private int widthSize;
    private int heightSize;

    /**
     * DottedEdgesCutCornerView
     *
     * @param context context
     */
    public DottedEdgesCutCornerView(Context context) {
        super(context);
        init(context, null);
    }

    /**
     * DottedEdgesCutCornerView
     *
     * @param context context
     * @param attrs attrs
     */
    public DottedEdgesCutCornerView(Context context, AttrSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    /**
     * DottedEdgesCutCornerView
     *
     * @param context context
     * @param attrs attrs
     * @param defStyleAttr defStyleAttr
     */
    public DottedEdgesCutCornerView(Context context, AttrSet attrs, int defStyleAttr) {
        super(context, attrs, String.valueOf(defStyleAttr));
        init(context, attrs);
    }

    private void init(Context context, AttrSet attrs) {
        display = DisplayManager.getInstance().getDefaultDisplay(context);
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.FILL_STYLE);
        paint.setStrokeWidth(10);
        paint.setStrokeJoin(Paint.Join.MITER_JOIN);
        if (attrs != null) {
            try {
                topLeftCutSize = attrs.getAttr("shape_dottedEdgesCutCorner_topLeftSize").get().getIntegerValue();
                topRightCutSize = attrs.getAttr("shape_dottedEdgesCutCorner_topRightSize").get().getIntegerValue();
                bottomLeftCutSize = attrs.getAttr("shape_dottedEdgesCutCorner_bottomLeftSize").get().getIntegerValue();
                bottomRightCutSize = attrs.getAttr("shape_dottedEdgesCutCorner_bottomRightSize").get().getIntegerValue();
                shape_position = attrs.getAttr("shape_position").get().getIntegerValue();
                String shape_edge_position = attrs.getAttr("shape_edge_position").get().getStringValue();
                String[] strings = getResourceManager().getElement(ResourceTable.Strarray_shape_edge_position).getStringArray();
                if ("left".equals(shape_edge_position)) {
                    dotEdgePosition = Integer.parseInt(strings[3]);
                } else if ("right".equals(shape_edge_position)) {
                    dotEdgePosition = Integer.parseInt(strings[4]);
                } else {
                    dotEdgePosition = Integer.parseInt(strings[POSITION_NONE]);
                }
                dotRadius = attrs.getAttr("shape_dot_radius").get().getIntegerValue();
                dotSpacing = attrs.getAttr("shape_dot_spacing").get().getIntegerValue();

            } catch (Exception e) {
                System.out.println(e.getLocalizedMessage());
            }
        }
        setEstimateSizeListener(this::onEstimateSize);
        addDrawTask(this::onDraw);
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        rectF.set(0, 0, getWidth(), getHeight());
        switch (shape_position) {
            case 0:
                paint.setColor(Color.WHITE);
                break;
            case 1:
                paint.setColor(new Color(Color.getIntColor("#FF4081")));
                break;
            case 2:
                paint.setColor(new Color(Color.getIntColor("#303F9F")));
                break;
        }
        Path path = generatePath(rectF, topLeftCutSize, topRightCutSize, bottomRightCutSize, bottomLeftCutSize);
        path.close();
        canvas.drawPath(path, paint);
    }

    @Override
    public boolean onEstimateSize(int i, int i1) {
        widthSize = EstimateSpec.getSize(i);
        heightSize = EstimateSpec.getSize(i1);
        return false;
    }

    private Path generatePath(Rect rect, float topLeftDiameter, float topRightDiameter, float bottomRightDiameter, float bottomLeftDiameter) {
        final Path path = new Path();

        topLeftDiameter = topLeftDiameter < 0 ? 0 : topLeftDiameter;
        topRightDiameter = topRightDiameter < 0 ? 0 : topRightDiameter;
        bottomLeftDiameter = bottomLeftDiameter < 0 ? 0 : bottomLeftDiameter;
        bottomRightDiameter = bottomRightDiameter < 0 ? 0 : bottomRightDiameter;

        path.moveTo(rect.left + topLeftDiameter, rect.top);
        if (containsFlag(POSITION_TOP)) {
            int count = 1;
            int x = (int) (rect.left + topLeftDiameter + dotSpacing * count + dotRadius * 2 * (count - 1));
            while (x + dotSpacing + dotRadius * 2 <= rect.right - topRightDiameter) {
                x = (int) (rect.left + topLeftDiameter + dotSpacing * count + dotRadius * 2 * (count - 1));
                path.lineTo(x, rect.top);
                path.quadTo(x + dotRadius, rect.top + dotRadius, x + dotRadius * 2, rect.top);
                count++;
            }
            path.lineTo(rect.right - topRightDiameter, rect.top);
        } else {
            path.lineTo(rect.right - topRightDiameter, rect.top);
        }
        path.lineTo(rect.right, rect.top + topRightDiameter);
        if (containsFlag(POSITION_RIGHT)) {
            //drawing dots starts from the bottom just like the LEFT side so that when using two
            //widgets side by side, their dots positions will match each other
            path.lineTo(rect.right - dotRadius, rect.top + topRightDiameter);
            path.lineTo(rect.right - dotRadius, rect.bottom - bottomRightDiameter);
            path.lineTo(rect.right, rect.bottom - bottomRightDiameter);

            int count = 1;
            int y = (int) (rect.bottom - bottomRightDiameter - dotSpacing * count - dotRadius * 2 * (count - 1));
            while (y - dotSpacing - dotRadius * 2 >= rect.top + topRightDiameter) {
                y = (int) (rect.bottom - bottomRightDiameter - dotSpacing * count - dotRadius * 2 * (count - 1));
                path.lineTo(rect.right, y);
                path.quadTo(rect.right - dotRadius, y - dotRadius, rect.right, y - dotRadius * 2);
                count++;
            }
            path.lineTo(rect.right, rect.top + topRightDiameter);
            path.lineTo(rect.right - dotRadius, rect.top + topRightDiameter);
            path.lineTo(rect.right - dotRadius, rect.bottom - bottomRightDiameter);
            path.lineTo(rect.right, rect.bottom - bottomRightDiameter);
        } else {
            path.lineTo(rect.right, rect.bottom - bottomRightDiameter);
        }
        path.lineTo(rect.right - bottomRightDiameter, rect.bottom);
        if (containsFlag(POSITION_BOTTOM)) {
            int count = 1;
            int x = (int) (rect.right - bottomRightDiameter - dotSpacing * count - dotRadius * 2 * (count - 1));
            while (x - dotSpacing - dotRadius * 2 >= rect.left + bottomLeftDiameter) {
                x = (int) (rect.right - bottomRightDiameter - dotSpacing * count - dotRadius * 2 * (count - 1));
                path.lineTo(x, rect.bottom);
                path.quadTo(x - dotRadius, rect.bottom - dotRadius, x - dotRadius * 2, rect.bottom);
                count++;
            }
            path.lineTo(rect.left + bottomLeftDiameter, rect.bottom);
        } else {
            path.lineTo(rect.left + bottomLeftDiameter, rect.bottom);
        }
        path.lineTo(rect.left, rect.bottom - bottomLeftDiameter);
        if (containsFlag(POSITION_LEFT)) {
            int count = 1;
            int y = (int) (rect.bottom - bottomLeftDiameter - dotSpacing * count - dotRadius * 2 * (count - 1));
            while (y - dotSpacing - dotRadius * 2 >= rect.top + topLeftDiameter) {
                y = (int) (rect.bottom - bottomLeftDiameter - dotSpacing * count - dotRadius * 2 * (count - 1));
                path.lineTo(rect.left, y);
                path.quadTo(rect.left + dotRadius, y - dotRadius, rect.left, y - dotRadius * 2);
                count++;
            }
            path.lineTo(rect.left, rect.top + topLeftDiameter);
        } else {
            path.lineTo(rect.left, rect.top + topLeftDiameter);
        }
        path.lineTo(rect.left + topLeftDiameter, rect.top);
        path.close();

        return path;
    }

    private boolean containsFlag(int positionFlag) {
        return (dotEdgePosition | positionFlag) == dotEdgePosition;
    }

    /**
     * getTopLeftCutSize
     *
     * @return 1L
     */
    public float getTopLeftCutSize() {
        return topLeftCutSize;
    }

    /**
     * setTopLeftCutSize
     *
     * @param topLeftCutSize topLeftCutSize
     */
    public void setTopLeftCutSize(float topLeftCutSize) {
        this.topLeftCutSize = topLeftCutSize;
        //requiresShapeUpdate();
        invalidate();
    }

    /**
     * getTopLeftCutSizeDp
     *
     * @return float
     */
    public float getTopLeftCutSizeDp() {
        return pxToDp(getTopLeftCutSize());
    }

    /**
     * setTopLeftCutSizeDp
     *
     * @param topLeftCutSize topLeftCutSize
     */
    public void setTopLeftCutSizeDp(float topLeftCutSize) {
        setTopLeftCutSize(dpToPx(topLeftCutSize));
    }

    /**
     * getTopRightCutSize
     *
     * @return float
     */
    public float getTopRightCutSize() {
        return topRightCutSize;
    }

    /**
     * setTopRightCutSize
     *
     * @param topRightCutSize topRightCutSize
     */
    public void setTopRightCutSize(float topRightCutSize) {
        this.topRightCutSize = topRightCutSize;
        //requiresShapeUpdate();
        invalidate();
    }

    /**
     * getTopRightCutSizeDp
     *
     * @return float
     */
    public float getTopRightCutSizeDp() {
        return pxToDp(getTopRightCutSize());
    }

    /**
     * setTopRightCutSizeDp
     *
     * @param topRightCutSize topRightCutSize
     */
    public void setTopRightCutSizeDp(float topRightCutSize) {
        setTopRightCutSize(dpToPx(topRightCutSize));
    }

    /**
     * getBottomRightCutSize
     *
     * @return float
     */
    public float getBottomRightCutSize() {
        return bottomRightCutSize;
    }

    /**
     * setBottomRightCutSize
     *
     * @param bottomRightCutSize bottomRightCutSize
     */
    public void setBottomRightCutSize(float bottomRightCutSize) {
        this.bottomRightCutSize = bottomRightCutSize;
        //requiresShapeUpdate();
        invalidate();
    }

    /**
     * getBottomRightCutSizeDp
     *
     * @return float
     */
    public float getBottomRightCutSizeDp() {
        return pxToDp(getBottomRightCutSize());
    }

    /**
     * setBottomRightCutSizeDp
     *
     * @param bottomRightCutSize bottomRightCutSize
     */
    public void setBottomRightCutSizeDp(float bottomRightCutSize) {
        this.setBottomRightCutSize(dpToPx(bottomRightCutSize));
    }

    /**
     * getBottomLeftCutSize
     *
     * @return float
     */
    public float getBottomLeftCutSize() {
        return bottomLeftCutSize;
    }

    /**
     * setBottomLeftCutSize
     *
     * @param bottomLeftCutSize bottomLeftCutSize
     */
    public void setBottomLeftCutSize(float bottomLeftCutSize) {
        this.bottomLeftCutSize = bottomLeftCutSize;
        //requiresShapeUpdate();
        invalidate();
    }

    /**
     * getBottomLeftCutSizeDp
     *
     * @return float
     */
    public float getBottomLeftCutSizeDp() {
        return pxToDp(getBottomLeftCutSize());
    }

    /**
     * setBottomLeftCutSizeDp
     *
     * @param bottomLeftCutSize bottomLeftCutSize
     */
    public void setBottomLeftCutSizeDp(float bottomLeftCutSize) {
        setBottomLeftCutSize(dpToPx(bottomLeftCutSize));
    }

    /**
     * getDotEdgePosition
     *
     * @return 1
     */
    public int getDotEdgePosition() {
        return dotEdgePosition;
    }

    /**
     * addDotEdgePosition
     *
     * @param dotEdgePosition dotEdgePosition
     */
    public void addDotEdgePosition(int dotEdgePosition) {
        this.dotEdgePosition |= dotEdgePosition;
        invalidate();
    }

    private float getDotRadius() {
        return dotRadius;
    }

    private void setDotRadius(float dotRadius) {
        this.dotRadius = dotRadius;
        invalidate();
    }

    private float getDotRadiusDp() {
        return pxToDp(getDotRadius());
    }

    private void setDotRadiusDp(float dotRadius) {
        setDotRadius(dpToPx(dotRadius));
    }

    private float getDotSpacing() {
        return dotSpacing;
    }

    private void setDotSpacing(float dotSpacing) {
        this.dotSpacing = dotSpacing;
        //requiresShapeUpdate();
        invalidate();
    }

    private float getDotSpacingDp() {
        return pxToDp(dotSpacing);
    }

    private void setDotSpacingDp(float dotSpacing) {
        setDotRadius(dpToPx(dotSpacing));
    }

    /**
     * dpToPx
     *
     * @param dp dp
     * @return float
     */
    protected float dpToPx(float dp) {
        return dp * display.get().getAttributes().densityPixels;
    }

    /**
     * pxToDp
     *
     * @param px px
     * @return float
     */
    protected float pxToDp(float px) {
        return px / display.get().getAttributes().densityPixels;
    }
}

